package dkbta.ontology.elements.abstractions.rate;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.abstractions.AbstractionDef;
import dkbta.ontology.elements.abstractions.TemporalSemanticProperty;
import dkbta.ontology.elements.abstractions.interpolation.PersistenceFunction;
import dkbta.ontology.elements.primitive.PrimitiveDef;
import dkbta.ontology.loading.OntologyException;
import dkbta.ontology.values.ScaledSymbolicValues;
import dkbta.ontology.values.ValuesType;
import dkbta.processing.ActionTimer;
import dkbta.processing.Patient;

public class RateDef extends AbstractionDef{
	/** The primitive the rate is abstracted from */
	private final String _abstractedFrom;

	private final RateMappingFunction _mappingFunction;

	/**
	 * Initializes a rate definition
	 * 
	 * @param name The rate's name
	 * @param componentType The rate's component type
	 * @param possibleValues The rate's possible values
	 * @param abstractedFrom The name of the primitive the rate is abstracted from
	 * @param necessaryContexts The contexts needed for the rate
	 * @param tempSemProperties The temporal semantics properties of this rate
	 * @param mapFunc The rate's mapping function
	 * @param persistFunc The rate's persistence function
	 */
	public RateDef(String name, String componentType,
		ScaledSymbolicValues possibleValues, String abstractedFrom,
		Set<String> necessaryContexts, Set<TemporalSemanticProperty> tempSemProperties,
		RateMappingFunction mapFunc, PersistenceFunction persistFunc){
		super(ElementType.RATE, name, componentType, possibleValues, necessaryContexts,
				tempSemProperties, persistFunc);
		_abstractedFrom = abstractedFrom;
		_mappingFunction = mapFunc;
	}

	@Override
	public void validate(Ontology ontology) throws OntologyException{
		super.validate(ontology);
		PrimitiveDef pd = ontology.getPrimitive(_abstractedFrom);
		if (pd == null){
			throw new OntologyException(
					"The rate if abstracted from a non-existing primitive: "
							+ _abstractedFrom);
		}
		ValuesType valueType = pd.getValueType();
		if (valueType != ValuesType.NUMERIC){
			throw new OntologyException(
					"The rate must abstracted from a numeric primitive. "
							+ _abstractedFrom + " is " + valueType);
		}
		_prerequisiteElements.add(pd);
	}

	@Override
	public String completeToString(){
		StringBuilder sb = new StringBuilder(super.completeToString());
		sb.append("\r\nAbstracted From: ").append(_abstractedFrom);
		sb.append("\r\nMapping Function: ").append(_mappingFunction);
		sb.append("\r\n");
		return sb.toString();
	}

	@Override
	public Rate newInstance(long startTime, long endTime, String[] value){
		Object validatedValue;
		if (value.length == 0
				|| (validatedValue = getValues().parseAndValidate(value[0])) == null){
			throw new IllegalArgumentException("Invalid value: " + Arrays.toString(value));
		}
		return new Rate(_name, startTime, endTime, validatedValue);
	}

	@Override
	protected void compute(Patient p){
		List<Rate> rates = p.getRates(_name);

		ActionTimer actionTimer = ActionTimer.getInstance();

		// Performing the mapping / abstraction
		int mappingActionId = actionTimer.actionStart();
		_mappingFunction.performMapping(p, rates, _name, _abstractedFrom,
			_necessaryContexts, _interpolation);
		actionTimer.actionEnd(mappingActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.MAPPING);

		// Performing the interpolation
		int interpolationActionId = actionTimer.actionStart();
		performInterpolation(rates);
		actionTimer.actionEnd(interpolationActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.INTERPOLATION);
	}
}
